﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static WindowsFormsApp1.userWin.WindowsHook;

namespace WindowsFormsApp1.userWin
{
    /// <summary>
    /// 具体参数请参考windows官网
    /// </summary>
    public class MyEvenDemo
    {

        /// <summary>
        ///    安装钩子
        /// </summary>
        /// <param name="idHook"> 钩子类型，即确定钩子监听何种消息，上面的代码中设为2，即监听键盘消息并且是线程钩子，如果是全局钩子监听键盘消息应设为13，线程钩子监听鼠标消息设为7，全局钩子监听鼠标消息设为14。</param>
        /// <param name="lpfn">钩子子程的地址指针。如果dwThreadId参数为0 或是一个由别的进程创建的线程的标识，lpfn必须指向DLL中的钩子子程。 除此以外，lpfn可以指向当前进程的一段钩子子程代码。钩子函数的入口地址，当钩子钩到任何消息后便调用这个函数</param>
        /// <param name="hInstance">应用程序实例的句柄。标识包含lpfn所指的子程的DLL。如果threadId 标识当前进程创建的一个线程，而且子程代码位于当前进程，hInstance必须为NULL。可以很简单的设定其为本应用程序的实例句柄。</param>
        /// <param name="threadId">与安装的钩子子程相关联的线程的标识符。如果为0，钩子子程与所有的线程关联，即为全局钩子。</param>
        /// <param name="gch">对应的Gc,因为windows的垃圾回收机制,怎的老子必须设置不回收并且把Gc抛出来,关闭hook的时候好回收</param>
        /// <returns></returns>
        public int Start(int WH_KEYBOARD_LL,HookProc KeyBoardHookProc, int hHook,out GCHandle gch)
        {
            gch = new GCHandle();
            // install Keyboard hook
            if (hHook == 0)
            {
                hHook = SetWindowsHookEx(WH_KEYBOARD_LL,
                KeyBoardHookProc,
                Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),
                0);
                gch = GCHandle.Alloc(KeyBoardHookProc);
                if (hHook == 0)
                {
                    Stop(hHook, gch);
                    throw new Exception("SetWindowsHookEx操作失败");
                }
                
            }
            return hHook;

        }


        /// <summary>
        /// 卸载钩子
        /// </summary>
        /// <param name="hHook">钩子对应的id</param>
        /// <param name="gch">对应Cg</param>
        /// <returns></returns>
        public int Stop(int hHook, GCHandle gch)
        {
            bool retKeyboard = true;
            if (hHook != 0)
            {
                gch.Free();
                retKeyboard = UnhookWindowsHookEx(hHook);
                hHook = 0;
            }
            //If UnhookWindowsHookEx fails.
            if (!retKeyboard) throw new Exception("UnhookWindowsHookEx failed.");
            return hHook;
        }


        /// <summary>
        /// 采用sendmessage注入事件
        /// </summary>
        /// <param name="hwnd">窗口ID</param>
        /// <param name="wMsg">按键</param>
        /// <param name="wParam">根据情况改变的参数(请参考windows官方文档的传参)</param>
        /// <param name="lParam">同上</param>
        /// <returns></returns>
        public int SendWindowsMessage(IntPtr hwnd, IntPtr wMsg, IntPtr wParam, IntPtr lParam)
        {
            //如果是弹起键
            if (wMsg == new IntPtr(MessageAPI.WM_KEYUP))
            {
                uint c = (uint)12 << 28;
                lParam = (IntPtr)(c + (uint)lParam);
            }
         
            return SendMessage(hwnd, wMsg, wParam, lParam);
        }

        /// <summary>
        /// 采用postmessage注入事件
        /// </summary>
        /// <param name="hwnd">窗口ID</param>
        /// <param name="wMsg">按键</param>
        /// <param name="wParam">根据情况改变的参数(请参考windows官方文档的传参)</param>
        /// <param name="lParam">同上</param>
        public int postWindowsMessage(IntPtr hwnd, IntPtr wMsg, IntPtr wParam, IntPtr lParam)
        {
            //如果是弹起键
            if (wMsg == new IntPtr(MessageAPI.WM_KEYUP))
            {
                uint c = (uint)12 << 28;
                lParam =(IntPtr)(c + (uint)lParam);
            }
           
            return PostMessage(hwnd, wMsg, wParam, lParam);
          
        }

        /// <summary>
        /// 根据id获取窗口
        /// </summary>
        /// <param name="lpClassName"></param>
        /// <param name="lpWindowName"></param>
        /// <returns></returns>
        public IntPtr FindWindowPtr(string lpClassName, string lpWindowName) {
            return FindWindow(lpClassName, lpWindowName);
        }

        /// <summary>
        /// 采用message注入事件的委托
        /// </summary>
        /// <param name="hwnd">窗口ID</param>
        /// <param name="wMsg">按键</param>
        /// <param name="wParam">根据情况改变的参数(请参考windows官方文档的传参)</param>
        public delegate void Message(IntPtr hwnd, IntPtr wMsg, IntPtr wParam, IntPtr lParam);  //声明了一个Delegate Type

        /// <summary>
        /// 采用message注入事件的委托
        /// </summary>
        /// <param name="time">时间</param>
        /// <param name="message">消息</param>
        /// <param name="hwnd">窗口ID</param>
        /// <param name="wMsg">按键</param>
        /// <param name="wParam">根据情况改变的参数(请参考windows官方文档的传参)</param>
        public void WiteTime(int time, Message message, IntPtr hwnd, IntPtr wMsg, IntPtr wParam, IntPtr lParam)
        {
            Thread.Sleep(time);
            ThreadPool.QueueUserWorkItem(new WaitCallback(delegate { message(hwnd, wMsg, wParam, lParam); }));
        }

        /// <summary>
        /// 将虚拟键代码转换（映射）为扫描代码或字符值
        /// </summary>
        /// <param name="uCode">虚拟密钥代码或扫描代码</param>
        /// <param name="uMapType">0.虚拟密钥代码，并转换为扫描代码虚拟密钥代码，并转换为扫描代码,1.扫描代码，并转换为虚拟键代码(2.3自己看文档,老子不想写了)</param>
        public uint MapWindowVirtualKey(uint uCode, uint uMapType)
        {
            return  MapVirtualKey(uCode, uMapType);
        }

        /// <summary>
        /// 设置内容
        /// </summary>
        /// <param name="ptr">窗口引用</param>
        /// <param name="s">字符串</param>
        public void setTextMessage(IntPtr ptr,string s)
        {
            foreach (char item in s)
            {
                postWindowsMessage(ptr, new IntPtr(MessageAPI.WM_CHAR), (IntPtr)item, (IntPtr)1);
            }
        }


        /// <summary>
        /// 创建用于使用指定的名称的装置的设备上下文
        /// </summary>
        /// <param name="pwszDriver"></param>
        /// <param name="pwszDevice"></param>
        /// <param name="pszPort"></param>
        /// <param name="DEVMODEA"></param>
        /// <param name=""></param>
        /// <returns></returns>
        public IntPtr CreateWindowDCA(string pwszDriver,string pwszDevice,string pszPort,IntPtr pdm)
        {
            return CreateDCA(pwszDriver, pwszDevice, pszPort, pdm); 
        }


        /// <summary>
        /// 截图
        /// </summary>
        /// <param name="intprt">窗口id</param>
        /// <param name="path">保存路径</param>
        /// <returns></returns>
       public bool ScreenShot(IntPtr intprt,string path)
       {
            //创建显示器的DC
            IntPtr intp = GetDC(intprt);
            Graphics graphics = Graphics.FromHdc(intp);
            //由一个指定设备的句柄创建一个新的Graphics对象
            Bitmap map = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, graphics);
            //根据屏幕大小创建一个与之相同大小的Bitmap对象
            Graphics graphics2 = Graphics.FromImage(map);
            //获得屏幕的句柄
            IntPtr graptr1 = graphics.GetHdc();
            //获得位图的句柄
            IntPtr graptr2 = graphics2.GetHdc();

            BitWinBlt(graptr2, 0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, graptr1, 0, 0, 13369376);
            //释放屏幕句柄
            graphics.ReleaseHdc(graptr1);
            //释放位图句柄
            graphics2.ReleaseHdc(graptr2);
            map.Save(path);
            return true;
        }

        /// <summary>
        ///   截图
        /// </summary>
        /// <param name="hdc">目标设备上下文的句柄</param>
        /// <param name="x">目标矩形左上角的x坐标（以逻辑单位表示</param>
        /// <param name="y">目标矩形左上角的y坐标</param>
        /// <param name="cx">源矩形和目标矩形的宽度</param>
        /// <param name="cy">源矩形和目标矩形的高度</param>
        /// <param name="hdcSrc">源设备上下文的句柄  </param>
        /// <param name="x1">源矩形左上角的x坐标</param>
        /// <param name="y1">源矩形左上角的y坐标</param>
        /// <param name="rop">栅格操作代码</param>
        /// <returns></returns>
        public bool BitWinBlt(IntPtr hdc, int x, int y, int cx, int cy, IntPtr hdcSrc, int x1, int y1, System.Int32 rop)
        {
            return BitBlt(hdc, x, y, cx, cy, hdcSrc, x1, y1, rop);
        }

    }

}
